#pragma once

#include "log.hpp"
#include "socket.hpp"
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include <poll.h>

using namespace std;

// const修饰的变量能够直接给数组定义长度
// static const int MAX_NUM = sizeof(fd_set) * 8;
static const int fd_MAX = 64;

static const uint16_t defaultport = 8088;
static const int defaultfd = -1;
static int _events_def = 0; // 默认请求和响应都是0
static int timeout = 3000;  // 设置成-1则是阻塞等待,0则是非阻塞轮询,其他则是以毫秒为单位的轮询

class PollServer
{
public:
    PollServer()
    {
        for (int i = 0; i < fd_MAX; i++)
        { 
            // 将每个结构体内的成员全部初始化
            _fds[i].fd = defaultfd;
            _fds[i].events = _events_def;
            _fds[i].revents = _events_def;
        }
    }

    void Init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen(); // 全连接队列有5个容量(自己的设置的)
    }

    void Accepter()
    {
        string cilentip;
        uint16_t cilentport;
        int sockfd = _listensock.Accept(&cilentip, &cilentport);

        int i = 1;
        for (; i < fd_MAX; i++)
        {
            // 如果数组里有没被占用的位置,就为新连接分配文件描述符fd
            if (_fds[i].fd != defaultfd)
                continue; // 直到找到没被占用的数组空间
            else
                break; // 此时找到了没被占用的数组空间
        }

        // 说明整个数组都没有空闲空间
        if (i == fd_MAX)
        {
            lg(Warning, "新连接无法分配,poll资源不够");
            exit(1);
        }

        // 此时的i是空闲的空间下标
        _fds[i].fd = sockfd;
        _fds[i].events = POLLIN;
    }

    // 读取所有连接的内容
    void Recer()
    {
        for (int i = 1; i < fd_MAX; i++)
        {
            // 如果该文件描述符被OS认为有数据可读
            if (_fds[i].revents & POLLIN)
            {
                char buffer[1024];
                ssize_t n = recv(_fds[i].fd, buffer, sizeof(buffer) - 1, 0); // 暂时不考虑粘包问题
                // 相关协议 encode decode...
                
                if(n > 0)
                {
                    buffer[n] = 0;
                    cout << "sockfd is: " << _fds[i].fd << " say: " << buffer;
                }
                else // 为作方便,不管是对方关闭连接还是读失败都作统一处理
                {
                    cout << "sockfd: " << _fds[i].fd << "quit " << endl;
                    close(_fds[i].fd);
                    _fds[i].fd = defaultfd;
                    _fds[i].events = _events_def;
                }
            }
            else continue;
        }
    }

    void Start()
    {
        _fds[0].fd = _listensock.Fd();
        _fds[0].events = POLLIN;
        while (true)
        {
            int n = poll(_fds, fd_MAX, timeout);

            if (n > 0) // 说明此时有数据可读
            {
                if (_fds[0].revents & POLLIN) // 说明此时有正在等待的连接
                    Accepter();                 // 把全连接队列里正在等待的连接给全部连接上

                else // 此时没有正在等待连接的连接,但是其他连接一定有内容可读
                    Recer();
            }
            else if (n == 0) // 说明此时不仅没有正在等待的连接并且连接里也没有内容可读取
                continue;
            else // 则select出错,目前不考虑
                break;
        }
    }

    ~PollServer()
    {
        _listensock.Close();
    }

private:
    uint16_t _port = defaultport;
    Sock _listensock;
    struct pollfd _fds[fd_MAX];
};